﻿using MvcSimplyCleverPart3.Models;
using MvcSimplyCleverResources;
using SimplyCleverMiddleTier;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Resources;
using System.Web;
using System.Web.Mvc;

namespace MvcSimplyCleverPart3
{
    public class SimplyCleverModelValidatorProvider : ModelValidatorProvider
    {
        CountryData[] metadataCountries = null;
        ResourceManager resourceManager = SimplyCleverResources.ResourceManager;

        internal static DataAnnotationsModelValidationFactory DefaultAttributeFactory =
        (metadata, context, attribute) => new DataAnnotationsModelValidator(metadata, context, attribute);


        internal static Dictionary<string, DataAnnotationsModelValidationFactory> AttributeFactories = new Dictionary<string, DataAnnotationsModelValidationFactory>()
        {
            {
                "Range",
                (metadata, context, attribute) => new RangeAttributeAdapter(metadata, context, (RangeAttribute)attribute)
            },
            {
                "RegularExpression",
                (metadata, context, attribute) => new RegularExpressionAttributeAdapter(metadata, context, (RegularExpressionAttribute)attribute)
            },
            {
                "Required",
                (metadata, context, attribute) => new RequiredAttributeAdapter(metadata, context, (RequiredAttribute)attribute)
            },
            {
                "StringLength",
                (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)
            },
            {
                "MaxLength",
                (metadata, context, attribute) => new StringLengthAttributeAdapter(metadata, context, (StringLengthAttribute)attribute)
            }
        };

        internal static Dictionary<string, Type> TypeFactory = new Dictionary<string, Type>
        {
            {"Range", typeof(RangeAttribute)},
            {"RegularExpression", typeof(RegularExpressionAttribute)},
            {"Required", typeof(RequiredAttribute)},
            {"StringLength", typeof(StringLengthAttribute)},
            {"MaxLength", typeof(StringLengthAttribute)}
        };

        public SimplyCleverModelValidatorProvider()
        {
            this.metadataCountries = CountryProvider.GetCountries();
        }


        public override IEnumerable<ModelValidator> GetValidators(ModelMetadata metadata, ControllerContext context)
        {
            var results = new List<ModelValidator>();

            // whether the validation is for a property or model 
            // (remember we can apply validation attributes to a property or model and same applies here as well)
            var isPropertyValidation = metadata.ContainerType != null && !String.IsNullOrEmpty(metadata.PropertyName);

            //metadata.ContainerType.GetProperty("CountryCode").GetValue("CountryCode")
            AddressViewModel address = context.Controller.ViewData.Model as AddressViewModel;
            string countryCode = string.Empty;
            
            if (address == null)
                countryCode = (string) context.Controller.ValueProvider.GetValue("CountryCode").ConvertTo(typeof(string));
               
            else
                countryCode = address.CountryCode;

            if (isPropertyValidation && !string.IsNullOrEmpty(countryCode))
            {
                var metadataCountry = metadataCountries.FirstOrDefault(mc => mc.Code.Equals(countryCode));

                if (metadataCountry.AddressFormatInfo.FieldRules.Any(fr => fr.FieldKey.Equals(metadata.PropertyName)))
                {
                    var rules = metadataCountry.AddressFormatInfo.FieldRules.FirstOrDefault(fr => fr.FieldKey.Equals(metadata.PropertyName)).Rules;

                    foreach (var rule in rules)
                    {
                        DataAnnotationsModelValidationFactory factory;

                        if (!AttributeFactories.TryGetValue(rule.Key, out factory))
                        {
                            factory = DefaultAttributeFactory;
                        }
                        var validatorType = TypeFactory[rule.Key];

                        var validator =  (ValidationAttribute)Activator.CreateInstance(validatorType, GetValidationArgs(rule));

                        validator.ErrorMessage = this.resourceManager.GetString("Error" + rule.MessageKey) ?? rule.MessageKey;
                        results.Add(factory(metadata, context, validator));
                    }
                }
            }


            return results;
        }

        private object[] GetValidationArgs(Rule rule)
        {
            var args = new object[rule.Parameters.Count()];
            var i = 0;

            foreach (var parameter in rule.Parameters)
            {
                switch (parameter.Key)
                {
                    case "Max":
                        args[i] = int.Parse(parameter.Value);
                        break;
                    default:
                        args[i] = parameter.Value;
                        break;
                }
                i++;
            }

            return args;
        }
    }
}